1 module spec;
2 import defs;
3 import util;
4 
5 enum IndexFile = "api/gl.xml";
6 enum PrependXMLFileLocation = "api/";
7 
8 void readInFunctionFamilies(ref OGLEnumGroup[] enums, ref OGLFunctionFamily[] families) {
9 	import std.file : readText;
10 	import std.experimental.xml;
11 	
12 	string raw_input = readText(IndexFile);
13 	
14 	auto domBuilder = raw_input
15 		.lexer
16 		.parser
17 		.cursor((CursorError err){})
18 		.domBuilder;
19 	domBuilder.setSource(raw_input);
20 	domBuilder.buildRecursive;
21 	auto dom = domBuilder.getDocument;
22 
23 	auto rootNode = dom.firstChild;
24 
25 	handleChildren(rootNode, enums, families);
26 
27 	debug(OGL_Spec) {
28 		import std.stdio;
29 		foreach(ref e; enums) {
30 			writeln("- ", e.name, e.isBitmask ? " [bitmask]": "");
31 			foreach(ref e2; e.enums) {
32 				if (e2.value !is null)
33 					writeln("  - ", e2.name, " = ", e2.value);
34 			}
35 		}
36 	}
37 }
38 
39 void handleChildren(T)(ref T rootNode, ref OGLEnumGroup[] enums, ref OGLFunctionFamily[] ret_Functions) {
40 	size_t emptyEnumGroup = -1;
41 	foreach(i, ref e; enums) {
42 		if (e.name is null)
43 			emptyEnumGroup = i;
44 	}
45 	if (emptyEnumGroup == -1) {
46 		emptyEnumGroup = enums.length;
47 		enums ~= OGLEnumGroup();
48 	}
49 
50 	size_t emptyFunctionFamily = -1;
51 	foreach(i, ref fg; ret_Functions) {
52 		if (fg.familyOfFunction is null)
53 			emptyFunctionFamily = i;
54 	}
55 	if (emptyFunctionFamily == -1) {
56 		emptyFunctionFamily = ret_Functions.length;
57 		ret_Functions ~= OGLFunctionFamily();
58 	}
59 
60 F1: foreach(child; rootNode.childNodes) {
61 		// Structure:
62 		//  - comment
63 		//  - types
64 		//  - groups (enums)
65 		//  - enums (value)
66 		//  - commands
67 		//  - feature
68 		//  - extensions
69 
70 		switch(child.nodeName) {
71 			case "comment":
72 				break;
73 			case "groups":
74 			F2: foreach(group; child.childNodes) {
75 					string name = group.attributes.getNamedItem("name").nodeValue;
76 				F3: foreach(ref eg; enums) {
77 						if (eg.name == name) {
78 						F4: foreach(child2; group.childNodes) {
79 								if (child2.nodeName == "enum") {
80 									string name2 = child2.attributes.getNamedItem("name").nodeValue;
81 									foreach(ref e; eg.enums) {
82 										if (e.name == name2) {
83 											continue F4;
84 										}
85 									}
86 
87 									eg.enums ~= OGLEnum(name2);
88 									continue F4;
89 								}
90 							}
91 
92 							continue F2;
93 						}
94 					}
95 
96 					enums ~= OGLEnumGroup(name);
97 					goto F3;
98 				}
99 				continue F1;
100 
101 			case "enums":
102 				string groupName, groupType;
103 
104 				if (child.attributes.getNamedItem("namespace").nodeValue == "OcclusionQueryEventMaskAMD") {
105 					groupName = "OcclusionQueryEventMaskAMD";
106 					groupType = "bitmask";
107 				} else if (child.attributes.getNamedItem("group") !is null) {
108 					groupName = child.attributes.getNamedItem("group").nodeValue;
109 					if (child.attributes.getNamedItem("type") !is null)
110 						groupType = child.attributes.getNamedItem("type").nodeValue;
111 				}
112 
113 				OGLEnumGroup* group;
114 				foreach(ref eg; enums) {
115 					if (eg.name == groupName) {
116 						group = ⪚
117 						break;
118 					}
119 				}
120 
121 				// no matter what we want to fill out all enums created
122 			F6: foreach(child2; child.childNodes) {
123 					if (child2.nodeName == "enum") {
124 						string name = child2.attributes.getNamedItem("name").nodeValue;
125 						string value = child2.attributes.getNamedItem("value").nodeValue;
126 						
127 						bool wasSet;
128 						foreach(ref group2; enums) {
129 							foreach(ref e; group2.enums) {
130 								if (e.name == name) {
131 									e.value = value;
132 									wasSet = true;
133 								}
134 							}
135 						}
136 						if (wasSet)
137 							continue F6;
138 						
139 						enums[emptyEnumGroup].enums ~= OGLEnum(name, value);
140 						continue F6;
141 					}
142 				}
143 
144 				if (group !is null)
145 					group.isBitmask = groupType == "bitmask";
146 				break;
147 
148 			case "commands":
149 			F7: foreach(child2; child.childNodes) {
150 					if (child2.nodeName == "command") {
151 						string returnType, name;
152 						string[] argTypes, argNames;
153 						size_t argI;
154 
155 						argTypes.length = child2.childNodes.length-1;
156 						argNames.length = child2.childNodes.length-1;
157 
158 						foreach(child3; child2.childNodes) {
159 							switch(child3.nodeName) {
160 								case "proto":
161 									size_t i;
162 									foreach(child4; child3.childNodes) {
163 										if (child4.nodeName == "name")
164 											break;
165 										
166 										if (i > 0)
167 											returnType ~= " ";
168 										
169 										if (child4.nodeValue !is null) {
170 											returnType ~= child4.nodeValue.fixTypePointer;
171 										}
172 										
173 										if (child4.childNodes.length > 0) {
174 											returnType ~= child4.firstChild.nodeValue.fixTypePointer;
175 										}
176 										
177 										i++;
178 									}
179 
180 									returnType = returnType.fixTypePointer;
181 
182 									name = child3.lastChild.firstChild.nodeValue;
183 									break;
184 								case "param":
185 									size_t i;
186 									foreach(child4; child3.childNodes) {
187 										if (child4.nodeName == "name")
188 											break;
189 
190 										if (i > 0)
191 											argTypes[argI] ~= " ";
192 
193 										if (child4.nodeValue !is null) {
194 											argTypes[argI] ~= child4.nodeValue.fixTypePointer;
195 										}
196 
197 										if (child4.childNodes.length > 0) {
198 											argTypes[argI] ~= child4.firstChild.nodeValue.fixTypePointer;
199 										}
200 
201 										i++;
202 									}
203 
204 									argTypes[argI] = argTypes[argI].fixTypePointer;
205 
206 									foreach(child4; child3.childNodes) {
207 										if (child4.nodeName == "name") {
208 											argNames[argI] = child4.firstChild.nodeValue;
209 											break;
210 										}
211 									}
212 									argI++;
213 									break;
214 								default:
215 									argTypes.length--;
216 									argNames.length--;
217 									break;
218 							}
219 						}
220 
221 						foreach(ref fg; ret_Functions) {
222 							foreach(ref fe; fg.functions) {
223 								if (fe.name == name) {
224 									continue F7;
225 								}
226 							}
227 						}
228 
229 						ret_Functions[emptyFunctionFamily].functions ~= OGLFunction(returnType, name, argTypes, argNames);
230 					}
231 				}
232 				break;
233 
234 			case "feature":
235 				if (child.attributes.getNamedItem("api").nodeValue == "gl") {
236 					string introducedInS = child.attributes.getNamedItem("number").nodeValue;
237 
238 					foreach(child2; child.childNodes) {
239 					F8: foreach(child3; child2.childNodes) {
240 							if (child3.nodeName == "command") {
241 								string name = child3.attributes.getNamedItem("name").nodeValue;
242 
243 								foreach(ref fg; ret_Functions) {
244 									foreach(ref fe; fg.functions) {
245 										if (fe.name == name && fe.introducedIn == OGLIntroducedIn.Unknown) {
246 											switch(introducedInS) {
247 												case "1.0": fe.introducedIn = OGLIntroducedIn.V1P0; break;
248 												case "1.1": fe.introducedIn = OGLIntroducedIn.V1P1; break;
249 												case "1.2": fe.introducedIn = OGLIntroducedIn.V1P2; break;
250 												case "1.3": fe.introducedIn = OGLIntroducedIn.V1P3; break;
251 												case "1.4": fe.introducedIn = OGLIntroducedIn.V1P4; break;
252 												case "1.5": fe.introducedIn = OGLIntroducedIn.V1P5; break;
253 												case "2.0": fe.introducedIn = OGLIntroducedIn.V2P0; break;
254 												case "2.1": fe.introducedIn = OGLIntroducedIn.V2P1; break;
255 												case "3.0": fe.introducedIn = OGLIntroducedIn.V3P0; break;
256 												case "3.1": fe.introducedIn = OGLIntroducedIn.V3P1; break;
257 												case "3.2": fe.introducedIn = OGLIntroducedIn.V3P2; break;
258 												case "3.3": fe.introducedIn = OGLIntroducedIn.V3P3; break;
259 												case "4.0": fe.introducedIn = OGLIntroducedIn.V4P0; break;
260 												case "4.1": fe.introducedIn = OGLIntroducedIn.V4P1; break;
261 												case "4.2": fe.introducedIn = OGLIntroducedIn.V4P2; break;
262 												case "4.3": fe.introducedIn = OGLIntroducedIn.V4P3; break;
263 												case "4.4": fe.introducedIn = OGLIntroducedIn.V4P4; break;
264 												case "4.5": fe.introducedIn = OGLIntroducedIn.V4P5; break;
265 												default: break;
266 											}
267 											continue F8;
268 										}
269 									}
270 								}
271 							}
272 						}
273 					}
274 				}
275 				break;
276 
277 			case "extensions":
278 				foreach(child2; child.childNodes) {
279 					string extName = child2.attributes.getNamedItem("name").nodeValue;
280 					
281 					foreach(child3; child2.childNodes) {
282 					F9: foreach(child4; child3.childNodes) {
283 							if (child4.nodeName == "command") {
284 								string name = child4.attributes.getNamedItem("name").nodeValue;
285 								foreach(ref fg; ret_Functions) {
286 									foreach(ref fe; fg.functions) {
287 										if (fe.name == name && fe.introducedInExtension is null) {
288 											fe.introducedInExtension = extName;
289 											continue F9;
290 										}
291 									}
292 								}
293 							}
294 						}
295 					}
296 				}
297 				break;
298 
299 			default:
300 				break;
301 		}
302 	}
303 }